from typing import List, Literal, Optional, cast
import json
import os
import requests
import urllib
from shared_types import *
import logging
logger = logging.getLogger(__name__)

class ESPObject:
    def __init__(self, ip: str, port: int) -> None:
        self.endpoint = f"http://{ip}:{port}"

    def listdir(self) -> List[FileInfoResponse]:
        res = requests.get(f"{self.endpoint}/fs/listdir")
        return res.json()

    def initialize(self, local_ip: str) -> None:
        data: InitializationData = {"control_station_ip": local_ip}
        response = requests.post(
            f"{self.endpoint}/ctrl/initialize", json=data, timeout=5
        )
        print("Initialized, got response:", response.text)
        return response.status_code < 300

    def resource_info(self) -> ResourceInfo:
        res = requests.get(f"{self.endpoint}/sys/resources_info", timeout=5)
        return res.json()

    def get_functional_configs(self) -> Optional[DeviceFunctionalConfig]:
        """
        Get configs from the ESP32
        """
        response = requests.get(
            f"{self.endpoint}/ctrl/get_functional_configs", timeout=5
        )
        if  response.status_code < 300:
            return response.json()
        else:
            logger.warning(f"Failed to get functional configs, code {response.status_code}, msg {response.text}")

    def upload_functional_configs(self, configs: DeviceFunctionalConfig) -> bool:
        """
        Uploads configs to the ESP32
        """
        response = requests.post(
            f"{self.endpoint}/ctrl/update_functional_configs", json=configs, timeout=5
        )
        print(response.text)
        return response.status_code < 300

    def set_speed(self, speed: int) -> None:
        response = requests.post(
            f"{self.endpoint}/ctrl/set_speed", json={"speed": speed}, timeout=5
        )
        return response.status_code < 300

    def set_magnet(self, index: int, state: BIDIR_SLFHLD_EM_STATES) -> None:
        req: ElectroMagnetRequest = {"state": state, "index": index}
        response = requests.post(
            f"{self.endpoint}/ctrl/set_magnet", json=req, timeout=15
        )
        print(response.status_code, response.text)
        return response.status_code < 300

    def upload_file(self, filename: str, new_name: str = "") -> bool:
        """
        upload file to esp32

        filename: path to file on local file system
        new_name: name of file on ESP32
        """
        if new_name == "":
            new_name = filename
        files = {"file": (new_name, open(filename, "rb"))}
        response = requests.post(
            f"{self.endpoint}/fs/upload", files=files, timeout=5)
        print(response.text)
        return response.status_code < 300

    def remove(self, file_or_dirname: str, type_: Literal["file", "directory"]) -> bool:
        """
        Remove file from the esp's filesystem
        """
        response = requests.post(
            f"{self.endpoint}/fs/remove",
            json={"type": type_, "name": file_or_dirname},
            timeout=5,
        )
        print(response.text)
        return response.status_code < 300

    def upload_code(self, directories_and_files: List[str], remove=False):
        allowed_suffixes = (".html", ".py", ".css", ".js", ".wav", ".mp3")
        for d in directories_and_files:
            if remove:
                ret = self.remove(
                    d, "directory" if os.path.isdir(d) else "file")
                if not ret:
                    print(f"Failed to remove {d}")
                    return
            if os.path.isdir(d):
                for root, _, files in os.walk(d):
                    for file in files:
                        if file.endswith(allowed_suffixes):
                            abspath = os.path.join(
                                root, file).replace("\\", "/")
                            print("trying to upload", abspath)
                            self.upload_file(abspath)
            else:
                self.upload_file(d)
